package com.hfzy.ihk.web.ivr.support.callin.handler;

import com.alibaba.fastjson.JSONObject;
import com.hfzy.ihk.common.util.Utils;
import com.hfzy.ihk.common.util.date.DateUtils;
import com.hfzy.ihk.common.util.string.StringTools;
import com.hfzy.ihk.common.util.string.StringUtil;
import com.hfzy.ihk.common.util.uuid.base64uuid.UUIDs;
import com.hfzy.ihk.facade.ivr.vo.CallHistory;
import com.hfzy.ihk.web.ivr.biz.FreeswitchAsrCallOutBiz;
import com.hfzy.ihk.web.ivr.biz.PushDataToEsBiz;
import com.hfzy.ihk.web.ivr.constant.Constants;
import com.hfzy.ihk.web.ivr.constant.RedisFields;
import com.hfzy.ihk.web.ivr.constant.RedisKey;
import com.hfzy.ihk.web.ivr.constant.RedisTimeOut;
import com.hfzy.ihk.web.ivr.support.callin.event.FreeswitchCallOutEvent;
import com.hfzy.ihk.web.ivr.support.disruptor.base.DisruptorHandlerAdapter;
import com.hfzy.ihk.web.ivr.websocket.WebSocketUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Created by Administrator on 2019/6/5.
 */
@Component
@Scope("prototype")
public class FreeswitchAsrCallOutHandler implements DisruptorHandlerAdapter<FreeswitchCallOutEvent> {

    Logger logger = LoggerFactory.getLogger(FreeswitchAsrCallOutHandler.class);

    @Autowired
    PushDataToEsBiz pushDataToEsBiz;

    @Autowired
    FreeswitchAsrCallOutBiz freeswitchAsrCallOutBiz;

    @Autowired
    RedisTemplate redisTemplate;

    @Resource
    TaskExecutor pushDataToEsTaskExecutor;

    @Autowired
    WebSocketUtils webSocketUtils;

    @Override
    public void onEvent(FreeswitchCallOutEvent event, long sequence, boolean endOfBatch) throws Exception {
        try {
            procces(event.getData());
        } catch (Exception e) {
            logger.error("ivr FreeswitchAsrCallOutHandler exception",e);
        }
    }

    @Override
    public void onEvent(FreeswitchCallOutEvent event) throws Exception {
        try {
            Long old = System.currentTimeMillis();
            procces(event.getData());
            old = System.currentTimeMillis()-old;
            logger.debug("ivr handle use time:{}",old);

        } catch (Exception e) {
            logger.error("ivr FreeswitchAsrCallOutHandler exception",e);
        }
    }

    /**
     * 流程方法
     *
     *
     * @param data*/
    private void procces(CallHistory data){
        String result = Constants.SUCCESS;
        //没有sessionId 生成sessionId
        if(StringUtil.isEmpty(data.getSessionId())) {

            try {
                String sessionId = UUIDs.base64UUID();
                if (sessionId == null) {
                    handleIdgeneratedFailed(data);
                    return;
                }
                data.setSessionId(sessionId);
            } catch (Exception e) {//读取redis生产id失败，通知客户端，失败
                handleIdgeneratedFailed(data);
                return;
            }
        }

        //写入redis
        try {
            writeRedis(data);

        } catch (Exception e) {
            logger.error("ivr writeRedis failed!",e);
            handleWriteRedisFailed(data);
        }

        //提交给云端
        try {
            logger.debug("ivr--> {} before commit to fs use sessionId:{}",data.getSessionId());
            Long old = System.currentTimeMillis();
            result= commit(data);
            old = System.currentTimeMillis()-old;
            logger.debug("ivr--> {} commit to fs use time:{},sessionId:{}",old,data.getSessionId());

            //呼叫失败推送给ES
            if(result.equals(Constants.FAIL)){
                commitFail(data);
            }

            //提交后生成的数据
            Map<String,String> writeRedisMap = new HashMap<>();

            writeRedisMap.put(RedisFields.CALL_HISTORY_TODAY,data.getToday());
            writeRedisMap.put(RedisFields.CALL_HISTORY_COMMIT_TIME, DateUtils.formatDate(data.getCommitTime(), DateUtils.DATE_FORMAT_DATETIME_SSS));

            if(!StringTools.isEmpty(data.getCallOutResultMessage()))
                writeRedisMap.put(RedisFields.CALL_OUT_RESULT_MESSAGE,data.getCallOutResultMessage());

            redisTemplate.opsForHash().putAll(RedisKey.CALL_HISTORY_RPIX + data.getSessionId(),writeRedisMap);
        } catch (Exception e) {
            logger.error( "ivr commit failed!",e);
        }


        /*after commit,现在无论提交成功失败都返回给client*/

        try {
            handleAfterCommit(data);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("ivr--> {} handleAfterCommit failed ! {}",e.getMessage());
        }

    }

    private void handleIdgeneratedFailed(CallHistory data) {
        logger.info("-->freeswitch 申请sessionId 失败");
    }

    /**
     * 写入redis失败
     * @param data
     * @return
     */
    private String handleWriteRedisFailed(CallHistory data) {
        logger.info("---->freeswitch 呼出时 写入 redis  失败 sessionId:{}",data.getSessionId());
        return null;
    }

    private String commit(CallHistory data) {
        String result = null;
        try {

            data.setCommitTime(new Date());  //呼出提交到freeswitch的时间

            Map<String,String> map = freeswitchAsrCallOutBiz.callOutOpt(data);


            if(map.get("msg")!=null){
                data.setCallOutResultMessage(map.get("msg"));
            }

            if(map.get("result").equals(Constants.SUCCESS)){
                data.setCommitOk(true);
                result = Constants.SUCCESS;
            }else{
                data.setCommitOk(false);
                data.setFailedReason(String.valueOf(map.get("msg")));
                result = Constants.FAIL;

            }
            try {
                data.setToday(Utils.date2DateString(data.getCreateTime()));
            }catch (Exception e){
                logger.error("--->set today fail",e);
            }

        }catch (Exception e){
            //异常这里因为不是调用api导致异常的所以返回成功
            logger.error(">>fs外呼异常",e);
            result = Constants.SUCCESS;
        }
        return result;
    }

    /**
     * 处理提交呼叫任务
     * 调用websocket通知客户端
     * */
    private void handleAfterCommit(CallHistory data) {

        //提交成功将当前呼叫的次数保存到redis 用来限制，每天一个客户号码只能呼一次的判断
        try {
            if(data.getPageFrom()!=null&&("1".equals(data.getPageFrom().toString())||"3".equals(data.getPageFrom().toString())||"10".equals(data.getPageFrom().toString()))) {
                logger.info(">>>phone:{} +1",data.getTargetCallPhone());
                redisTemplate.opsForHash().increment(RedisKey.TODAY_CALL_COUT, data.getTargetCallPhone(), 1);
            }
        }catch (Exception e){
            logger.error("ivr redis 增加呼叫限制，异常：{}",e);
        }

        //提交成功推送给客户端
        Map<String,String> map = new HashMap<>();
        String nowStr = DateUtils.formatDate(new Date(), DateUtils.DATE_FORMAT_DATETIME_SSS);
        if(data.getCommitOk()){
            map.put(Constants.WEB_SOCKET_JSON_KEY_MSG_TYPE, Constants.WEB_SOCKET_MSG_TYPE_COMMIT_SUCCESS);
            /*//缓存最后使用某主显号码拨打给某客户号码的信息
            try {
                callHistoryLastUtils.addCallHistoryLastByShowNum(data.getMainCallPhone(), data.getTargetCallPhone(),
                        data.getCreateUserId().toString(), data.getDataId().toString(), data.getCustomerName(), nowStr,
                        data.getPackageId(),data.getCallTaskId(),data.getCity().toString());

            }catch (Exception e){
                logger.error("缓存最后使用的主显号码失败,:sessionId:{}",data.getSessionId(),e);
            }*/
        }else {
            map.put(Constants.WEB_SOCKET_JSON_KEY_MSG_TYPE, Constants.WEB_SOCKET_MSG_TYPE_COMMIT_FAILED);
            map.put(Constants.WEB_SOCKET_JSON_KEY_DESC, data.getCallOutResultMessage());
        }
        map.put(Constants.WEB_SOCKET_JSON_KEY_TIME, nowStr);
        map.put(Constants.WEB_SOCKET_JSON_KEY_SESSION_ID, data.getSessionId());
        map.put(Constants.WEB_SOCKET_JSON_KEY_DATA_ID, String.valueOf(data.getDataId()));
        map.put(Constants.WEB_SOCKET_JSON_KEY_PAGE_FROM, String.valueOf(data.getPageFrom()));
        map.put(Constants.WEB_SOCKET_JSON_KEY_MACHINE_FROM, data.getMachineFrom());
        //将状态推到客户端
        webSocketUtils.pushToClientByToken(data.getMachineFrom(), JSONObject.toJSONString(map));

    }

    /**
     * 呼出后写入redis
     * @param data
     * @return
     */
    private String writeRedis(CallHistory data) {
        //创建记录时间
        String createTimeStr = DateUtils.formatDate(data.getCreateTime(), DateUtils.DATE_FORMAT_DATETIME_SSS);

        String sessionId = data.getSessionId();


        String redisKey= RedisKey.CALL_HISTORY_RPIX+sessionId;

        Map<String,String> writeRedisMap=new HashMap<>();

        logger.info("ivr writed redis,sessionId：{}", data.getSessionId());
        ///封装需要写入redis的数据
        writeRedisMap.put(RedisFields.CALL_HISTORY_SESSION_ID,data.getSessionId());
        writeRedisMap.put(RedisFields.CREATE_USER_ID, data.getCreateUserId().toString());
        writeRedisMap.put(RedisFields.CREATE_USER_NO,data.getCreateUserNo().toString());
        writeRedisMap.put(RedisFields.CREATE_TIME, createTimeStr);
        writeRedisMap.put(RedisFields.CALL_HISTORY_SHOW_NUM, data.getShowNum());
        writeRedisMap.put(RedisFields.CALL_HISTORY_MAIN_CALL_PHONE, data.getMainCallPhone());
        writeRedisMap.put(RedisFields.CALL_HISTORY_DATA_ID, data.getDataId().toString());

        writeRedisMap.put(RedisFields.CALL_HISTORY_MACHINE_FROM, data.getMachineFrom());
        writeRedisMap.put(RedisFields.CALL_HISTORY_WHERE_FROM, data.getWhereFrom().toString());
        writeRedisMap.put(RedisFields.CALL_HISTORY_PAGE_FROM, data.getPageFrom().toString());
        writeRedisMap.put(RedisFields.CALL_HISTORY_MAIN_CALL_TYPE, data.getMainCallType().toString());
        writeRedisMap.put(RedisFields.CALL_HISTORY_CALL_TYPE, data.getCallType().toString());
        writeRedisMap.put(RedisFields.CALL_HISTORY_CITY, data.getCity().toString());

        writeRedisMap.put(RedisFields.CALL_HISTORY_CUSTOMER_NAME, data.getCustomerName());
        writeRedisMap.put(RedisFields.CALL_HISTORY_COMMIT_TO, data.getCommitTo().toString());
        writeRedisMap.put(RedisFields.CALL_HISTORY_PROJECT, data.getProject() == null ? "NULL" : data.getProject());
        writeRedisMap.put(RedisFields.CALL_HISTORY_PROPERTY_NO, data.getPropertyNo() == null ? "NULL" : data.getPropertyNo());
        writeRedisMap.put(RedisFields.CALL_HISTORY_OBJECT_ID, data.getObjectId() == null ? "NULL" : data.getObjectId());
        writeRedisMap.put(RedisFields.CALL_HISTORY_COMMIT_TIME, DateUtils.formatDate(data.getCommitTime(), DateUtils.DATE_FORMAT_DATETIME_SSS));
        writeRedisMap.put(RedisFields.CALL_TASK_ID, data.getCallTaskId() == null ? "NULL" : data.getCallTaskId());
        writeRedisMap.put(RedisFields.PACKAGE_ID,data.getPackageId()==null?"null":data.getPackageId());
        writeRedisMap.put(RedisFields.PACKAGE_NAME,data.getPackageName()==null?"null":data.getPackageName());

        if(StringTools.isNotempty(data.getVoiceCode()))
            writeRedisMap.put(RedisFields.VOICE_CODE,data.getVoiceCode());

        if(StringTools.isNotempty(data.getVoiceText()))
            writeRedisMap.put(RedisFields.VOICE_TEXT,data.getVoiceText());


        writeRedisMap.put(RedisFields.CALL_HISTORY_COMMIT_OK,"true"); //这里默认全部提交成功


        if(logger.isDebugEnabled()) {
            for (String b : writeRedisMap.keySet()) {
                logger.debug("ivr before write redis hash--> key:" + b + " value:" + writeRedisMap.get(b));
            }
        }
        /*保存呼叫数据,3天 推送成功以后会自动清除*/
        redisTemplate.opsForHash().putAll(redisKey,writeRedisMap);
        redisTemplate.expire(redisKey, RedisTimeOut.CACHE_THREE_DAY, TimeUnit.SECONDS);
        //保存当天客户电话被呼次数

        try{
            //目前默认电子客进来
            //pushDataToEsTaskExecutor.execute(new RmiPushCallTimeTask(data));
        }catch (Exception e){

            logger.error("ivr >>fs推送次数到缓存服务器异常",e);

        }



        //将taskId和sessionId的关系保持到redis 一天过期
        logger.info("ivr ---->外呼获得的whereFrom:"+data.getWhereFrom());

        //目前有些呼叫是没有packageId,都会将他们写死为0,这些通话不需要语音分析
        if (!"0".equals(data.getPackageId())){
            redisTemplate.opsForValue().set(RedisKey.ALIBABA_ASR+data.getSessionId(), data.getPackageId() + ":" + data.getCallTaskId(), RedisTimeOut.CACHE_ONE_DAY, TimeUnit.SECONDS);
        }

        writeRedisMap = null;
        return Constants.SUCCESS;
    }


    /**
     * 提交失败直接入库
     * @param data
     * @return
     */
    private String commitFail(CallHistory data) {

        try {
            Map<String,String> map = redisTemplate.opsForHash().entries(RedisKey.CALL_HISTORY_RPIX +data.getSessionId());
            String jsonStr = JSONObject.toJSONString(map);
            pushDataToEsTaskExecutor.execute(() -> {
                pushDataToEsBiz.pushCallHistoryToEs(data.getSessionId(), jsonStr,false);
            });
            return Constants.SUCCESS;
        }catch (Exception e){
            try {
                redisTemplate.opsForSet().add(RedisKey.PUSH_TO_ES_FAILED_HISTORY,data.getSessionId());
            }catch (Exception e1){
                logger.error("ivr 将推送ES 失败数据放入redis 失败！sessionId：{}",data.getSessionId(),e1);
            }
            logger.error("ivr ====>推送Es 的线程池爆了",e);
            return Constants.FAIL;
        }

    }
}
